{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Step 2 - 测试模型\n",
    "\n",
    "在这个文档里，我们会使用我们在Step 1中训练的模型在AirSim 中驱动一辆车。我们将对模型的性能进行一些观测，提出一些改进模型的潜在实验的建议。\n",
    "\n",
    "首先我们导入一些库。  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "from keras.models import load_model\n",
    "import sys\n",
    "import numpy as np\n",
    "\n",
    "if ('../../PythonClient/' not in sys.path):\n",
    "    sys.path.insert(0, '../../PythonClient/')\n",
    "from AirSimClient import *\n",
    "\n",
    "# << 模型的目录 >>  \n",
    "MODEL_PATH = 'model/models/sample_model.h5'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "然后，我们将载入模型，连接AirSim 模拟器到景观图环境。请确保在结束不同进程中*之前*模拟器一直在运行。  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Waiting for connection: \n"
     ]
    }
   ],
   "source": [
    "model = load_model(MODEL_PATH)\n",
    "\n",
    "client = CarClient()\n",
    "client.confirmConnection()\n",
    "client.enableApiControl(True)\n",
    "car_controls = CarControls()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们将设置汽车的初始状态和一些用于从模型存储输出的缓冲。  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "car_controls.steering = 0\n",
    "car_controls.throttle = 0\n",
    "car_controls.brake = 0\n",
    "\n",
    "image_buf = np.zeros((1, 59, 255, 3))\n",
    "state_buf = np.zeros((1,4))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们将定义一些帮助函数来从AirSim 中读取RGB 图片，然后准备给模型使用。  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def get_image():\n",
    "    image_response = client.simGetImages([ImageRequest(0, AirSimImageType.Scene, False, False)])[0]\n",
    "    image1d = np.fromstring(image_response.image_data_uint8, dtype=np.uint8)\n",
    "    image_rgba = image1d.reshape(image_response.height, image_response.width, 4)\n",
    "    \n",
    "    return image_rgba[76:135,0:255,0:3].astype(float)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "最后，一个控制框来执行汽车运行。因为我们的模型没有预测速度，我们将试着保持我们的汽车运行在匀速5 m/s。执行下面这个框将使得模型驱动汽车！  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "while (True):\n",
    "    car_state = client.getCarState()\n",
    "    \n",
    "    if (car_state.speed < 5):\n",
    "        car_controls.throttle = 1.0\n",
    "    else:\n",
    "        car_controls.throttle = 0.0\n",
    "    \n",
    "    image_buf[0] = get_image()\n",
    "    state_buf[0] = np.array([car_controls.steering, car_controls.throttle, car_controls.brake, car_state.speed])\n",
    "    model_output = model.predict([image_buf, state_buf])\n",
    "    car_controls.steering = round(0.5 * float(model_output[0][0]), 2)\n",
    "    \n",
    "    print('Sending steering = {0}, throttle = {1}'.format(car_controls.steering, car_controls.throttle))\n",
    "    \n",
    "    client.setCarControls(car_controls)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 观察和之后的试验\n",
    "\n",
    "搞定了！汽车在道路上跑的很完美，在大部分时刻都保持在正确形式一侧，很谨慎的导航所有大转向时刻和所有它可能偏离道路的可能的时刻。然而，你将突然注意到一些其他事情。首先，汽车移动很平缓，尤其在那些桥。然后，如果你让模型运行一会儿（超过5分钟一会），你将注意到汽车逐渐随机偏离道路造成碰撞。但不必沮丧！记住我们在表面上基本没有碰到这些问题。事实上，能够让汽车学会用非常小的数据集几乎完美地行驶，这件事本身是值得骄傲的！\n",
    "\n",
    "> **思考训练 2.1**：    \n",
    "尽管你可能已经发现，汽车在那些桥上没有很平缓运行。你能想出是什么原因才这样？你能用一些在Step 0 中介绍的技术来解决这个问题么？  \n",
    "\n",
    "> **思考训练 2.2**：  \n",
    "当汽车试着爬那些小山的一部分时，似乎会产生碰撞。你能想出原因嘛？你如何解决这个问题呢？（提示：你可能想去看汽车发生碰撞时汽车在看什么）  \n",
    "\n",
    "AirSim 打开了一个充满可能性的世界。即使你想训练更复杂的模型或者使用其他的学习策略都不会限制你尝试任何新鲜事情。这里是一些最直接的事情你能够去尝试的，可能需要你修改本文档中提供的一些代码（包括帮助函数），但是不会要求一些不现实的条件。  \n",
    "\n",
    "> **探索想法 2.1**：  \n",
    "如果你有机器学习背景，你应该已经问过这样一个问题：为什么我们在同样一个环境中进行训练和测试？那不会过拟合嘛？好，你可以在两边都做论证。尽管看起来使用相同的环境进行训练和测试让你在此环境中产生过拟合，但是它也可以被看作是从相同概率分布中抽取样本。训练和测试数据不同，尽管他们来自相同的分布。所以这样我们产生一个疑问：如果模型在一个从来没有遇见过的环境中，会有怎样的情况？  \n",
    "如果给一个其他的可能得非常不同的汽车从未遇到过的环境，当前这个模型可能表现不会很好（路口，交通道路，建筑等）。但是让它在这样的环境中表现好对它不公平。你想，让一个从来没有在山路驾驶经验的人，或者在他有生之年从来没有见过其他汽车或者路口，突然让他在城市里去驾驶。你能想象他们会做得有多好？  \n",
    "相反的情况应该很有趣。训练从城市环境收集的数据会更轻易用于山路么？试着自己解决。  \n",
    "\n",
    "> **探索想法 2.2**：  \n",
    "我们用回归问题来表述这个问题 - 我们在预测一个连续变化的变量。相反，我们能够用分类问题来表述这个问题。更精确来说，我们能够给转向角定义一串数(..., -0.1, -0.05, 0, 0.05, 0.1, ...)，bucketize 标签，给每一张图片预测正确的bucket。如果这样改变会发生什么？  \n",
    "\n",
    "> **探索想法 2.3**：  \n",
    "当前模型每次看一个单张模型和单独一个状态来进行一个预测。然而，我们可以适用所有历史数据。我们可以根据之前的N 张图片和状态（比如给定过去3 张图片和过去3 个状态，来预测下一个转向角）？（提示：这可能需要你使用RNN 模型）  \n",
    "\n",
    "> **探索想法 2.4**：  \n",
    "AirSim 比我们提供的数据集要多得多。对新手，我们仅仅使用一个摄像头，仅使用RGB 模式。AirSim 可以让你在每一个可能的摄像机采集深度图，分割图，surface normal view等。所以你在每一个实例场景（我们这里仅使用一张图片）可以有20个不同的图片（5 个摄影机在4 种模式）。怎样组合这些信息帮我们提升我们要训练的模型？  "
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
